package main

import (
	"fmt"
	"reflect"
)

// 定义学生结构体
type Student struct {
	Name string
	Age  int
}

// 给结构体绑定方法：
func (s Student) CPrint() {
	fmt.Println("调用了Print()方法")
	fmt.Println("学生的名字是：", s.Name)
}
func (s Student) AGetSum(n1, n2 int) int {
	fmt.Println("调用了AGetSum方法")
	return n1 + n2
}
func (s Student) BSet(name string, age int) {
	s.Name = name
	s.Age = age
}

func main() {
	//对基本数据类型进行反射：
	//定义一个基本数据类型：
	//修改变量值传入指针
	var num int = 100
	testReflect(num)
	testReflectUpdate(&num)
	fmt.Println("修改后的变量值是：", num)

	//对结构体类型进行反射：
	//定义结构体具体的实例：
	//获取变量类型
	stu := Student{
		Name: "aa",
		Age:  20,
	}
	testReflectStruct(stu)
	// 调用结构体的方法
	TestStudentStruct(stu)
}

// 定义函数操作结构体进行反射操作：
func TestStudentStruct(a interface{}) {
	//a转成reflect.Value类型：
	val := reflect.ValueOf(a)
	fmt.Println(val)

	//通过reflect.Value类型操作结构体内部的字段：
	n1 := val.NumField()
	fmt.Println(n1)
	//遍历-获取具体的字段：
	for i := 0; i < n1; i++ {
		fmt.Printf("第%d个字段的值是：%v", i, val.Field(i))
	}
	fmt.Println()

	//通过reflect.Value类型操作结构体内部的方法：
	n2 := val.NumMethod()
	fmt.Println(n2)

	//调用CPrint()方法：
	//调用方法，方法的首字母必须大写才能有对应的反射的访问权限
	//方法的顺序按照ASCII的顺序排列的，a,b,c,,,,,,索引：0,1,2，，，，，
	val.Method(2).Call(nil)

	//调用AGetSum方法：
	//定义Value的切片：
	var params []reflect.Value
	params = append(params, reflect.ValueOf(10))
	params = append(params, reflect.ValueOf(20))
	result := val.Method(0).Call(params)
	fmt.Println("AGetSum方法的返回值为：", result[0].Int())
}

// 利用一个函数，函数的参数定义为空接口：
// 空接口没有任何方法,所以可以理解为所有类型都实现了空接口，也可以理解为我们可以把任何一个变量赋给空接口。
func testReflectUpdate(i interface{}) {
	reValue := reflect.ValueOf(i)

	//通过SetInt()来改变值：
	reValue.Elem().SetInt(2000)
}

// 利用一个函数，函数的参数定义为空接口：
// 空接口没有任何方法,所以可以理解为所有类型都实现了空接口，也可以理解为我们可以把任何一个变量赋给空接口。
func testReflectStruct(stu interface{}) {
	//1.调用TypeOf函数，返回reflect.Type类型数据：
	reType := reflect.TypeOf(stu)
	fmt.Println("reType:", reType)
	fmt.Printf("reType的具体类型是：%T\n", reType)

	//获取变量类型
	k1 := reType.Kind()
	fmt.Println("reType.Kind():", k1)

	//2.调用ValueOf函数，返回reflect.Value类型数据：
	reValue := reflect.ValueOf(stu)
	fmt.Println("reValue:", reValue)
	fmt.Printf("reValue的具体类型是：%T\n", reValue)

	k2 := reValue.Kind()
	fmt.Println("reValue.Kind():", k2)

	//reValue转成空接口：
	is := reValue.Interface()
	//类型断言 _省略返回参数
	n, _ := is.(Student)
	fmt.Printf("学生的名字是：%v,学生的年龄是：%v\n", n.Name, n.Age)
	fmt.Printf("结构体类型是：%T", n)
}

// 利用一个函数，函数的参数定义为空接口：
// 空接口没有任何方法,所以可以理解为所有类型都实现了空接口，也可以理解为我们可以把任何一个变量赋给空接口。
func testReflect(i interface{}) {
	//1.调用TypeOf函数，返回reflect.Type类型数据：
	reType := reflect.TypeOf(i)
	fmt.Println("reType:", reType)
	fmt.Printf("reType的具体类型是：%T \n", reType)

	//2.调用ValueOf函数，返回reflect.Value类型数据：
	reValue := reflect.ValueOf(i)
	fmt.Println("reValue:", reValue)
	fmt.Printf("reValue的具体类型是：%T \n", reValue)

	//如果真想获取reValue的数值，要调用Int()方法：返回v持有的有符号整数
	num2 := 80 + reValue.Int()
	fmt.Println(num2)

	//reValue转成空接口：
	i2 := reValue.Interface()
	//类型断言
	n, flag := i2.(int)
	if flag {
		n2 := n + 30
		fmt.Println(n2)
	}
}
